<?php

namespace MatchEngine;

class OrderRedis
{

    private $redis;
    private $host;  //redis ip
    private $port;  //redis 端口
    private $auth;  //redis 端口

    public $orderDb;  // 订单缓存库
    public $orderBookDb;  //盘口缓存库


    public function __construct($config)
    {
        $this->host = $config['host'];
        $this->port = $config['port'];
        $this->auth = $config['auth'];
        $this->orderDb = $config['orderDb'];
        $this->orderBookDb = $config['orderBookDb'];
        //连接redis
        if (class_exists('Redis')) {
            $this->redis = new \Redis();
            $this->redis->pconnect($this->host, $this->port);
            if (strlen($this->auth)) {
                $this->redis->auth($this->auth);
            }
            if (!$this->redis->ping()) {
                exit('redis cannot connect');
            }

        } else {
            exit('redis扩展不存在');
        }
    }

    /**
     * 盘口-添加订单
     * @param string $market 交易市场（交易对）
     * @param string $type 订单类型[limit,market]
     * @param string $side [买单bid,卖单ask]
     * @param $order_id
     * @param float $price 价格
     * @return int
     */
    public function newOrderBook(string $market, string $type, string $side, $order_id, float $price)
    {
        $this->redis->select($this->orderBookDb);
        $key = 'orderbook:' . $market . ':' . $type . ':' . $side;
        return $this->redis->zAdd($key, $price, $order_id);
    }

    /**
     * 盘口-数据列表(price降序)
     * @param string $market 交易市场（交易对）
     * @param string $type [买单bid,卖单ask]
     * @param string $side 订单类型[limit,market]
     * @return array
     */
    public function getOrderBooks(string $market, string $type, string $side)
    {
        $this->redis->select($this->orderBookDb);
        $key = 'orderbook:' . $market . ':' . $type . ':' . $side;
        return $this->redis->zRevRange($key, 0, -1, true);
    }

    /**
     * 盘口-查找指定价格区间的盘
     * @param string $market 交易市场（交易对）
     * @param string $type 订单类型[limit,market]
     * @param string $side [买单bid,卖单ask]
     * @param int $fromPrice 最小价格（包含）  区间的取值使用闭区间 (小于等于或大于等于)
     * @param int $toPrice 最大价格（包含）
     * @return array
     */
    public function getPriceArea(string $market, string $type, string $side, int $fromPrice, int $toPrice)
    {
        $this->redis->select($this->orderBookDb);
        $key = 'orderbook:' . $market . ':' . $type . ':' . $side;
        return $this->redis->zRangeByScore($key, $fromPrice, $toPrice, ['withscores' => TRUE]);
    }

    /**
     * 盘口-根据订单编号查找价格
     * @param string $market 交易市场（交易对）
     * @param string $type 订单类型[limit,market]
     * @param string $side [买单bid,卖单ask]
     * @param $order_id
     * @return bool|float
     */
    public function getOrderBook(string $market, string $type, string $side, $order_id)
    {
        $this->redis->select($this->orderBookDb);
        $key = 'orderbook:' . $market . ':' . $type . ':' . $side;
        return $this->redis->zScore($key, $order_id);
    }

    /**
     * 盘口-指定订单编号的价格加减运算
     * @param string $market 交易市场（交易对）
     * @param string $type 订单类型[limit,market]
     * @param string $side [买单bid,卖单ask]
     * @param $order_id
     * @param $price float 要增加（正数）或 减少（负数）的价格数量
     * @return float 修改后的价格
     */
    public function operaOrderBook(string $market, string $type, string $side, $order_id, float $price)
    {
        $this->redis->select($this->orderBookDb);
        $key = 'orderbook:' . $market . ':' . $type . ':' . $side;
        return $this->redis->zIncrBy($key, $price, $order_id);
    }

    /**
     * 盘口-删除指定订单
     * @param string $market 交易市场（交易对）
     * @param string $type 订单类型[limit,market]
     * @param string $side [买单bid,卖单ask]
     * @param $order_id
     * @return int
     */
    public function removeOrderBook(string $market, string $type, string $side, $order_id)
    {
        $this->redis->select($this->orderBookDb);
        $key = 'orderbook:' . $market . ':' . $type . ':' . $side;
        return $this->redis->zRem($key, $order_id);
    }

    /**
     * 订单-新增
     * @param $order_id
     * @param array $data 订单的内容键值对，包括[order_id,user_id,market,price,quantity,side,type]
     * @return bool
     */
    public function newOrder($order_id, array $data)
    {
        $this->redis->select($this->orderDb);
        if (!is_array($data)) {
            return false;
        }
        $data['status'] = 0;//订单状态
        $data['match_id'] = '';//与本单撮合成功的订单id,逗号分隔
        $resOrder = $this->redis->hMSet($order_id, $data);
        $resOrderBook = $this->newOrderBook($data['market'], $data['type'], $data['side'], $order_id, $data['price']);//新建订单同时插入盘口
        return $resOrder;
    }

    /**
     * 订单-查找指定编号订单指定字段值
     * @param $order_id
     * @param string $field 要查询的订单字段名，可选[order_id,user_id,market,price,quantity,side,type]
     * @return mixed
     */
    public function getOrderField($order_id, string $field)
    {
        $this->redis->select($this->orderDb);
        return $this->redis->hMGet($order_id, [$field])[$field];
    }

    /**
     * 订单-查找指定编号订单
     * @param $order_id
     * @return array 新增的订单信息
     */
    public function getOrder($order_id)
    {
        $this->redis->select($this->orderDb);
        return $this->redis->hGetAll($order_id);
    }

    /**
     * 订单-指定字段值的加减运算
     * @param $order_id
     * @param string $field 字段名
     * @param float $num 运算数量(正数+，负数-)
     * @return float    运算后的结果
     */
    public function operaOrder($order_id, string $field, float $num)
    {
        $this->redis->select($this->orderDb);
        return $this->redis->hIncrByFloat($order_id, $field, round($num, 4));
    }

    /**
     * 订单-修改指定订单字段值
     * @param $order_id
     * @param string $field 字段名
     * @param $value
     * @return bool
     */
    public function updateOrder($order_id, $field, $value)
    {
        $this->redis->select($this->orderDb);
        return $this->redis->hMSet($order_id, [$field => $value]);
    }

    /**
     * 订单-删除指定编号的订单
     * @param $order_id
     * @return bool
     */
    public function removeOrder($order_id)
    {
        $this->redis->select($this->orderDb);
        return $this->redis->del($order_id);
    }

    /**
     * 查寻Redis所有key
     * @return array    keys数组
     */
    public function getKeys()
    {
        $this->redis->select($this->orderDb);
        return $this->redis->keys('*');
    }

    /**
     * 清空Redis所有数据
     * @return int  清空的数量
     */
    public function cleanAll()
    {
        $this->redis->select($this->orderBookDb);
        $this->redis->flushdb();
        $this->redis->select($this->orderDb);
        $this->redis->flushdb();
        return $this->redis->flushdb();
    }

    /**
     * 订单列表
     * @return array
     */
    public function listOrder()
    {
        $keys = $this->getKeys();
        $list = [];
        foreach ($keys as $key => $value) {
            $list[] = $this->getOrder($value);
        }
        return $list;
    }


}